// 1.6.1 函数-Lambda 函数与表达式
/**
 * C++ 函数
 * 函数是一组一起执行一个任务的语句。每个 C++ 程序都至少有一个函数，即主函数 main() ，所有简单的程序都可以定义其他额外的函数。
 * 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的，但在逻辑上，划分通常是根据每个函数执行一个特定的任务来进行的。
 * 函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。 
 * C++ 标准库提供了大量的程序可以调用的内置函数。例如，函数 strcat() 用来连接两个字符串，函数 memcpy() 用来复制内存到另一个位置。
 * 函数还有很多叫法，比如方法、子例程或程序，等等。
 * 
 * ---------------------
 * Lambda 函数与表达式
 * 译 Lambda n. 希腊字母表的第11个字母 ；读 [ˈlæmdə] 兰姆达
 * 
 * C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。
 * Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用，比如可以将它们赋给变量和作为参数（普通函数可以作为参数传递吗？当然可以）传递，还可以像函数一样对其求值。
 * 
 * Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下:
 * [capture](parameters)->return-type{body}
 * 译: capture vt.俘虏;俘获;捕获;用武力夺取;攻取;攻占;夺得;赢得;争得  n.(被)捕获;(被)俘获
 * 读: [ˈkæptʃə(r)] 
 * 例如：
 * [](int x, int y){ return x < y ; }
 * 
 * 如果没有返回值可以表示为：
 * [capture](parameters){body}
 * 例如：
 * []{ ++global_x; } 
 * 
 * 在一个更为复杂的例子中，返回类型可以被明确的指定如下：
 * [](int x, int y) -> int { int z = x + y; return z + x; }
 * 本例中，一个临时的参数 z 被创建用来存储中间结果。如同一般的函数，z 的值不会保留到下一次该不具名函数再次被调用时。
 * 
 * 如果 lambda 函数没有传回值（例如 void），其返回类型可被完全忽略。
 * 
 * 在Lambda表达式内可以访问当前作用域的变量，这是Lambda表达式的闭包（Closure）行为。 与JavaScript闭包不同，C++变量传递有传值和传引用的区别。可以通过前面的[]来指定：
 * 译: Closure 闭包;闭合;封闭性;关闭;结束
 * 读: [ˈkləʊʒə(r)]
    []      // 沒有定义任何变量。使用未定义变量会引发错误。
    [x, &y] // x以传值方式传入（默认），y以引用方式传入。
    [&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
    [=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
    [&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
    [=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
 * 
 * 另外有一点需要注意。对于[=]或[&]的形式，lambda 表达式可以直接使用 this 指针。但是，对于[]的形式，如果要使用 this 指针，必须显式传入：
 * [this]() { this->someFunc(); }();
 * 
 * ~~~~~~~~~~~~~~~~~~~~~~~~
 * lambda函数使用
*/
#include <iostream>
#include <functional>
//  = NULL
// stdL::function是个模板类；
// 模板参数：返回值类型+函数参数类型列表；例如std::function<int  (int, int)> f，f可以指向任意一个返回指为int，参数为两个int类型的函数。

// 观察main，lambda表达式无参数，返回值类型为void；故在test函数中声明一个类型为function<void ()>类型的变量fun，就可以用来接收my_out。注意：fun前面必须加上const，否则编译过不了。至于加不加&，需要视程序而定，一般加上。
// auto &fun 不行
// lambda函数默认是const函数；mutable 可以取消其常量性。在使用该修饰符时，参数列表不可省略（即使参数为空）。
void test(int num, std::function<void()> const &fun = NULL)
{
  std::cout << "num : " << num << std::endl;

  if (fun)
  {
    std::cout << "传函数了" << std::endl;
    fun();
  }
}

void func1(void); // 普通函数声明

int main(void)
{
  // [capture](parameters)->return-type{body}
  // 可以赋值给变量或作为参数传递
  // 不能用int 声明 max
  // ！！必须用auto定义变量来接收lambda函数；有其他的方式吗？
  auto max = [](int a, int b) -> int
  {
    return a > b ? a : b;
  };

  int val = max(1, 2);
  std::cout << "val : " << val << std::endl;

  // 不能用void声明swap
  // 无返回值时可以省略 -> void
  auto swap = [](int &a, int &b)
  {
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
  };
  int a = 3, b = 4;
  std::cout << "交换前 "
            << "a : " << a << " b : " << b << std::endl;
  swap(a, b);
  std::cout << "交换后 "
            << "a : " << a << " b : " << b << std::endl;

  // 无参无返回值
  auto my_out = []
  {
    std::cout << "hello" << std::endl;
  };
  my_out();

  // ！！！报错不能在一个函数里定义函数 a function-definition is not allowed here before '{' token {
  // void test(int num, int *fun = NULL)
  // {
  //   std::cout << "num : " << num << std::endl;
  //   if (*fun)
  //   {
  //     std::cout << "传函数了" << std::endl;
  //     *fun();
  //   }
  // }

  test(1, func1);

  // 作为函数参数传递
  test(2, my_out);

  // ****像函数一样对其求值。
  []
  {
    std::cout << "自执行函数" << std::endl;
  }();

  std::cout << ([]() -> std::string
                { return "返回值是……"; }())
            << std::endl;

  // 这种情况 未指明返回值，但他返回值时有效的
  auto test2 = []
  {
    // int a = 3;
    float a = 3.123;
    return a;
  };

  // val = 0;
  // val = test2();

  float val_f = 0.0;
  val_f = test2();

  // std::cout << "有效的return val: " << val << std::endl;
  std::cout << "有效的return val_f: " << val_f << std::endl;

  // []() -> void { return "test"; }(); //这种指明了return void类型再用return会报错

  /**
   * lambda函数总结1：
   * 0. lambda表达式，也叫lambda函数，用来定义使用匿名函数
   * 1. 可以赋值给一个变量，定义auto型变量接收
   * 2. 可以作为函数参数传入到函数里，并在函数中使用。（需要引入functional;函数形参定义格式 std::function<return_type (int,char)> const p_name ;其中return_type 是要传入的函数返回值类型 int、char是 传入的函数参数类型，p_name是形参名称 ,注意必须使用const修饰形参名） 
   * 3. 可以像函数一样使用，在表达式后面加(),或者通过在保存该函数的变量
   * 后加()来调用它
   * 4. 语法
   * [capture](parameters)->return-type{body}
   * 
   * 无返回值时可省写
   * [capture](parameters){body}
   * 
   * 无返回值、无参数时可省写（!!!注意 该表达式中也可以使用return）
   * [capture]{body}
   * 
   * 有返回值时，无参数 不可省写！！！
   * 
  */

  // int num = 0;
  // auto counter = []
  // {
  //   num++; // error: 'num' is not captured
  // };

  // counter();
  // std::cout << "num : " << std::endl;

  printf("---------end-----------");
  return 0;
}

// 函数定义
void func1(void)
{
  std::cout << "普通函数" << std::endl;
}
